﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UniFramework.Event;
using UnityEngine;
using YooAsset;
using HybridCLR;
using System.Linq;

public class Boot : MonoBehaviour
{
    /// <summary>
    /// 资源系统运行模式
    /// </summary>
    public EPlayMode PlayMode = EPlayMode.EditorSimulateMode;

    void Awake()
    {
        Debug.Log($"资源系统运行模式：{PlayMode}");
        Application.targetFrameRate = 60;
        Application.runInBackground = true;
        DontDestroyOnLoad(this.gameObject);
    }
    IEnumerator Start()
    {

        // 初始化事件系统
        UniEvent.Initalize();

        // 初始化资源系统
        YooAssets.Initialize();

        // 加载更新页面
        var go = Resources.Load<GameObject>("PatchWindow");
        GameObject.Instantiate(go);

        // 开始补丁更新流程
        PatchOperation operation = new PatchOperation("DefaultPackage", PlayMode);
        YooAssets.StartOperation(operation);
        yield return operation;


        //更新热更代码
        PatchOperation operation_hotUpdate = new PatchOperation("HotUpdatePackage", PlayMode);
        YooAssets.StartOperation(operation_hotUpdate);
        yield return operation_hotUpdate;

        // 加载元数据和热更代码全部加载到内存中
        yield return LoadHotUpdateRes();
        LoadMetadataForAOTAssemblies();


        // 设置默认的资源包
        var gamePackage = YooAssets.GetPackage("DefaultPackage");
        YooAssets.SetDefaultPackage(gamePackage);

        // 切换到主页面场景
        //SceneEventDefine.ChangeToHomeScene.SendEventMessage();

        Type entryType = _hotUpdateAss.GetType("GameManager");
        Debug.Log("EntryType" + entryType);

        MethodInfo method = entryType.GetMethod("LoadHomeScene");
        if (method == null)
        {
            Debug.LogError("LoadHomeScene method not found.");
            yield break;
        }
        Debug.Log("methodInfo" + method.Name);
        entryType.GetMethod("LoadHomeScene").Invoke(null, null);
    }

    private static Dictionary<string, byte[]> s_assetDatas = new Dictionary<string, byte[]>();

    private static List<string> AOTMetaAssemblyFiles { get; } = new List<string>()
    {
        "Cinemachine.dll.bytes",
        "DOTween.dll.bytes",
        "UniFramework.Event.dll.bytes",
        "Unity.InputSystem.dll.bytes",
        "UnityEngine.CoreModule.dll.bytes",
        "YooAsset.dll.bytes",
        "mscorlib.dll.bytes",

        "HotUpdate.dll.bytes",
    };
    private static Assembly _hotUpdateAss;
    public static byte[] ReadBytesFromStreamingAssets(string dllName)
    {
        Debug.Log("HotUpdate.dll.bytes");
        return s_assetDatas[dllName];
    }
    private IEnumerator LoadHotUpdateRes()
    {
        var hotUpdatePackage = YooAssets.GetPackage("HotUpdatePackage");

        foreach (var dll in AOTMetaAssemblyFiles)
        {
            RawFileHandle handle = hotUpdatePackage.LoadRawFileAsync($"Assets/GameResHotUpdate/{dll}");
            yield return handle;
            var bytes = handle.GetRawFileData();
            s_assetDatas[dll] = bytes;
        }
    }
    /// <summary>
    /// 为aot assembly加载原始metadata， 这个代码放aot或者热更新都行。
    /// 一旦加载后，如果AOT泛型函数对应native实现不存在，则自动替换为解释模式执行
    /// </summary>
    private static void LoadMetadataForAOTAssemblies()
    {
        /// 注意，补充元数据是给AOT dll补充元数据，而不是给热更新dll补充元数据。
        /// 热更新dll不缺元数据，不需要补充，如果调用LoadMetadataForAOTAssembly会返回错误
        /// 
        HomologousImageMode mode = HomologousImageMode.SuperSet;
        foreach (var aotDllName in AOTMetaAssemblyFiles)
        {
            byte[] dllBytes = ReadBytesFromStreamingAssets(aotDllName);
            // 加载assembly对应的dll，会自动为它hook。一旦aot泛型函数的native函数不存在，用解释器版本代码
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
            Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
        }
#if !UNITY_EDITOR
        _hotUpdateAss = System.Reflection.Assembly.Load(ReadBytesFromStreamingAssets("HotUpdate.dll.bytes"));
#else
        _hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate");
        Debug.Log($"HotUpdate Assembly:{_hotUpdateAss.FullName}");
#endif
    }
} 

